/**********************************************************************************************************************
 * @file    mtk_series.c
 * @author  Queclink Jason.Huang
 * @date    2022-08-31
 * @brief
 *
 * Copyright (C) 2021 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 *
 * @attention
 *
 * @usage
 *
 *********************************************************************************************************************/

/* includes ---------------------------------------------------------------------------------------------------------*/
#include <common/qlcommon.h>
#include <gnss_public.h>
#include <gnss_info_process.h>
#include <gnss_command.h>
#include <nmea.h>
#include "pair.h"
#include "bsp_gnss.h"

#include "gnss_log.h"
#ifdef __cplusplus
extern "C" {
#endif

#define CMD_TIMEOUT_MS              1000
#define READ_BUF_SIZE               128

extern char gnss_fw_ver[GNSS_FW_VER_LEN+1];

static const unsigned int baudrate_arr[]={921600,115200,230400,460800,3000000};
unsigned int pairBaudrate = 0;
unsigned char pairGnssMode = 0;
bool mtkseries_ready = 0;

void mtkSeries01_PowerOn(void)
{
    void *read_buf = NULL;

    read_buf = qlmalloc(READ_BUF_SIZE);
    if (NULL == read_buf)
        return;

    while (bsp_gnss_read(read_buf, READ_BUF_SIZE) > 0);
    qlfree(read_buf);

    /* 模块上电 */
    gnss_trace(LOG_INFO"Power on.\r\n");
    bsp_gnss_power_on();
}

void mtkSeries01_PowerOff(void)
{
    bsp_gnss_power_off();
    gnss_trace(LOG_INFO"Power off.\r\n");
    mtkseries_ready = false;
}

void mtkSeries01_Reboot(int mode)
{
    if (GNSS_COLD_START == mode)
        pairCommandSend(PAIR_SUBSYS_FULLCOLD, NULL);
    else if (GNSS_WARM_START == mode)
        pairCommandSend(PAIR_SUBSYS_WARM, NULL);
    else
        pairCommandSend(PAIR_SUBSYS_HOT, NULL);
}

#define SYSTEMON_SUBSYS_ON_CMD_RETRY_CNT    cps->param[0]

int mtkSeries01_SystemOn(gnss_cmd_cps *cps)
{
    gnss_task_manage_t *gps_device = get_gnss_manage();

    /* Wakeup 拉高超过 10ms, 平时保持低 */
    switch(cps->stage) {
    case 0:         /* VCC ON */
        mtkSeries01_PowerOn();
        cps->stage++;
        cps->uptime = getms();
        break;

    case 1:
        if (!timeout_ms(cps->uptime, 100))
            break;

        pairCommandSend(PAIR_SUBSYS_ON, NULL);          /* 快速发出唤醒指令，避免模块又进入睡眠 */
        cps->stage++;
        cps->uptime = getms();
        gps_device->comm_err.lost_timeout = getms();
        break;

    case 2:
        if(timeout_ms(cps->uptime, 500)){
            if (SYSTEMON_SUBSYS_ON_CMD_RETRY_CNT++ >= 6){
                gnss_trace(LOG_ERROR "GNSS wakeup failed\r\n");
                return -EPERM;
            }
            pairCommandSend(PAIR_SUBSYS_ON, NULL);      /* 快速发出唤醒指令，避免模块又进入睡眠 */
            cps->uptime = getms();
        }
        break;

    case 3:
        return 0;

    default:
        return 0;

    }
    return -EBUSY;
}

/* 检查初始化结束后的状态, 发布初始化状态事件 */
#define CONFIGDONE_GET_VER_CMD_RETRY_CNT      cps->param[0]
int mtkSeries01_ConfigDone(gnss_cmd_cps *cps)
{
    switch (cps->stage) {
    case 0:                                     /* 查询固件版本 */
        pairCommandSend(PAIR_GET_VERSION, NULL);
        cps->uptime = getms();
        cps->stage++;
        break;

    case 1:
        if(timeout_ms(cps->uptime, CMD_TIMEOUT_MS)){
            if(CONFIGDONE_GET_VER_CMD_RETRY_CNT++ >= 3){                        /* 重试三次 */
                /* 无法读取版本, 通讯失败 */
                mtkseries_ready = true;
                gnss_trace(LOG_ERROR"Initialization failure.\r\n");
                return -EPERM;
            }
            cps->stage--;
        }
        break;

    case 2:
        gnss_trace(LOG_INFO"Initialization completed.\r\n");
        mtkseries_ready = true;
        /* 保存参数 */
        pairCommandSend(PAIR_CMD_SAVE_SETTING, NULL);
        pairCommandSend(PAIR_GET_EPO_STATUS, NULL);

        cps->uptime = getms();
        cps->stage++;
        break;

    case 3:
        if(timeout_ms(cps->uptime, CMD_TIMEOUT_MS))
            return -EPERM;
        break;

    default:
        return 0;
    }

    return -EBUSY;
}

int mtkSeries01_CmdMessage(gnss_cmd_cps *cps)
{
    switch (cps->stage) {
    case 0:
        /* 关闭串口流控 */
        pairCommandSend(PAIR_IO_SET_FLOW_CONTROL, NULL);

        cps->stage++;
        cps->uptime = getms();
        break;

    case 1:
        if(timeout_ms(cps->uptime, CMD_TIMEOUT_MS)){
            return 0;
        }
        break;

    default:
        return 0;
    }
    return -EBUSY;
}

int mtkSeries01_Baudrate(gnss_cmd_cps *cps)
{
    gnss_task_manage_t * gnss_manage = get_gnss_manage();
    unsigned int baudrate = QLSDK_GNSS_COMM_BAUD;

    switch(cps->stage){
    case 0:
        cps->uptime = getms();
        cps->stage++;
        break;

    case 1:
        /* 切换 MCU 波特率 */
        if (!gnss_manage->gnss_info.is_uart_rx_ok && !timeout_ms(cps->uptime, 2000))
            break;

        cps->param[0] = 0;
        /* 查询当前波特率 */
        pairBaudrate = 0;
        pairCommandSend(PAIR_IO_GET_BAUDRATE, NULL);
        cps->stage++;
        cps->uptime = getms();

        break;

    case 2:
        /* 等待应答 */
        if (timeout_ms(cps->uptime, 1000)){
            /* 通讯失败 */
            gnss_trace(LOG_DEBUG"UART Communicate error.\r\n");
            if (cps->param[0] < sizeof(baudrate_arr)/sizeof(baudrate_arr[0])){
                /* 切换波特率 */
                bsp_gnss_set_baudrate(baudrate_arr[cps->param[0]]);
                gnss_trace(LOG_DEBUG"Set mcu baudrate: %d.\r\n",baudrate_arr[cps->param[0]]);
                cps->param[0]++;
                cps->uptime = getms();
                pairBaudrate = 0;
                pairCommandSend(PAIR_IO_GET_BAUDRATE, NULL);
            }else{
                bsp_gnss_set_baudrate(baudrate);
                gnss_trace(LOG_DEBUG"Set mcu baudrate: 115200.\r\n");
                gnss_trace(LOG_DEBUG"Have try all possible baudrate,but still communicate err!\r\n");
                return -1;
            }
        } else if (0 != pairBaudrate) {
            if (pairBaudrate == baudrate) {
                gnss_trace(LOG_DEBUG"Baud Rate unchanged.\r\n");
                return 0;
            } else {
                cps->stage++;
                cps->uptime = getms();
            }
        }

        break;

    case 3:
        /*设置新的波特率*/
        pairCommandSend(PAIR_IO_SET_BAUDRATE, (void*)baudrate);
        gnss_trace(LOG_DEBUG "Configure baudrate:%d\r\n",baudrate);
        vTaskDelay(_MS2TICKS(5));
        /*需要重启设备才能配置成功*/
        pairCommandSend(PAIR_SYSTEM_REBOOT, NULL);

        cps->stage++;
        cps->uptime = getms();
        break;

    case 4:
        /* 指令可能还没有发送完成 */
        if(timeout_ms(cps->uptime, 500)){
            bsp_gnss_set_baudrate(baudrate);
            gnss_trace(LOG_DEBUG"Set mcu baudrate:%d.\r\n",baudrate);
            cps->stage++;
            cps->uptime = getms();
        }

        break;

    case 5:             /*设备重启，需要较长时间*/
        if(timeout_ms(cps->uptime, 2000)){
            /* 发送测试指令是否通信正常 */
            pairCommandSend(PAIR_GET_VERSION, NULL);
            cps->stage++;
            cps->uptime = getms();
        }
        break;

    case 6:
        if(timeout_ms(cps->uptime, 500)){
            gnss_manage->gnss_info.is_uart_rx_ok = false;
            return 0;
        }
        break;

    default:
        return 0;
    }
    return -EBUSY;
}

/* QZSS 搭配 GPS 使用,默认开启
 */
int mtkSeries01_GnssMode(gnss_cmd_cps *cps)
{
    unsigned char gnssMode = 0;

    /* 双频模块, 仅允许配置, GREB/GEB, 否则失去双频的性能, QZSS常开
     * 配置该指令后, 设备会重启,需要检查配置是否改变后再进行配置
     */
    switch(cps->stage){
    case 0:             /* 读取当前配置 */
        pairGnssMode = 0;
        pairCommandSend(PAIR_GET_GNSS_SEARCH, NULL);
        cps->stage++;
        cps->uptime = getms();
        break;

    case 1:             /* 等待应答 */
        if(timeout_ms(cps->uptime, 1000) || 0 != pairGnssMode){
            if ((pairGnssMode&0xF) == (GNSS_MODE_DEFAULT_CONF&0xF)) {
                gnss_trace(LOG_DEBUG"GNSS mode configuration unchanged.\r\n");
                return 0;
            } else {
                cps->stage++;
            }
        }
        break;

    case 2:             /* 配置新的模式 */
        gnssMode = GNSS_MODE_DEFAULT_CONF;
        gnss_trace(LOG_DEBUG "configure new gnss : 0x%X\r\n", gnssMode);
        pairCommandSend(PAIR_SET_GNSS_SEARCH, (void*)gnssMode);

        cps->stage++;
        cps->uptime = getms();

        break;

    case 3:             /* 设备可能重启 */
        if(timeout_ms(cps->uptime, 3000)){
            gnss_trace(LOG_DEBUG "configure gnss mode timeout.\r\n");
            return 0;
        }
        break;

    default:
        return 0;
    }
    return -EBUSY;
}

void mtkS01_pair_parse(int argc, char *argv[])
{
    gnss_cmd_list_t *cmd = NULL;
    gnss_task_manage_t * gnss_manage = get_gnss_manage();
    int ack;
    int cmd_num;

    if(!gnss_manage->gnss_info.is_uart_rx_ok)
        gnss_manage->gnss_info.is_uart_rx_ok = true;

    cmd = gnss_cmd_get_first();             /* 获取当前执行的指令 */

    /* ACK */
    if (NULL != strstr(argv[0],"001")) {
        if (argc < 3)
            return;

        /* 获取 ACK 结果 */
        ack = atoi(argv[2]);
        cmd_num = atoi(argv[1]);
        gnss_trace(LOG_DEBUG "PAIR ACK: Command '%s', Flag '%d'.\r\n",argv[1], ack);

        if (ack != PAIR_ACK_PROCCESSING) {
            if (NULL != cmd) {
                if((GNSS_CMD_CONFIG_DONE == cmd->id && (PAIR_GET_VERSION == cmd_num || PAIR_CMD_SAVE_SETTING == cmd_num))\
                    || (GNSS_CMD_MSG_SET == cmd->id && PAIR_IO_SET_FLOW_CONTROL == cmd_num)\
                    || (GNSS_CMD_GNSS_MODE_SET == cmd->id && PAIR_SET_GNSS_SEARCH == cmd_num)\
                    || (GNSS_CMD_SYSTEM_ON == cmd->id && PAIR_SUBSYS_ON == cmd_num)){
                        if(PAIR_CMD_SUCCESS == ack) {
                            cmd->cps.stage++;
                        }
                        return;
                }
            }
        }
        return;
    }


    /* 查询波特率应答 */
    if (NULL != strstr(argv[0],"865")) {
        pairBaudrate = atoi(argv[1]);
        gnss_trace(LOG_DEBUG "GNSS baudrate %s.\r\n",argv[1]);
    }

    /* 查询 GNSS mode 应答 */
    if (NULL != strstr(argv[0],"067")) {
        if (argv[1][0] == '1')
            pairGnssMode |= GNSS_GPS;
        if (argv[2][0] == '1')
            pairGnssMode |= GNSS_GLONASS;
        if (argv[3][0] == '1')
            pairGnssMode |= GNSS_GALILEO;
        if (argv[1][0] == '1')
            pairGnssMode |= GNSS_BEIDOU;
        gnss_trace(LOG_DEBUG "GNSS mode{%s,%s,%s,%s,%s,%s}.\r\n",argv[1],argv[2],argv[3],argv[4],argv[5],argv[6]);
    }

    /* 系统启动标志 */
    if (NULL != strstr(argv[0],"011")) {
        gnss_trace(LOG_DEBUG "GNSS system startup.\r\n");
    }

    if (NULL != strstr(argv[0],"020")) {                /* PAIR_GET_VERSION */
        if (NULL != cmd) {
            if(GNSS_CMD_BAUDRADE == cmd->id)
                cmd->cps.stage++;
        }

        if (strlen(argv[1])) {
            strcpy(gnss_fw_ver, argv[1]);
            gnss_trace(LOG_DEBUG "Software version:%s\r\n", gnss_fw_ver);
        }
    }

    if(NULL != strstr(argv[0], "470")){
        gnss_trace(LOG_INFO "EPO Response data: System ID=%s,Set=%s,FWN=%s,FTOW=%s,LWN=%s,LTOW=%s,FCWN=%s,FCTOW=%s,LCWN=%s,LCTOW=%s\r\n",
                                                            argv[1], argv[2],
                                                            argv[3], argv[4],
                                                            argv[5], argv[6],
                                                            argv[7], argv[8],
                                                            argv[9], argv[10]);
    }

    /* 系统唤醒标志 */
    if (NULL != strstr(argv[0],"012")) {
        gnss_trace(LOG_DEBUG "GNSS system wakeup.\r\n");
    }
}

/* 命令列表
 */
const struct gnss_cmd_table_t mtkSeries01_CmdTable[] = {
    {GNSS_CMD_SYSTEM_ON,        mtkSeries01_SystemOn},
    {GNSS_CMD_GNSS_MODE_SET,    mtkSeries01_GnssMode},
    {GNSS_CMD_MSG_SET,          mtkSeries01_CmdMessage},
    {GNSS_CMD_BAUDRADE,         mtkSeries01_Baudrate},
    {GNSS_CMD_CONFIG_DONE,      mtkSeries01_ConfigDone},
};

const unsigned short mtkSeries01_CmdTableCnt =\
    sizeof(mtkSeries01_CmdTable)/sizeof(mtkSeries01_CmdTable[0]);

void *mtkSeries01_CmdSearch(int cmd_id)
{
    for (int i=0; i<mtkSeries01_CmdTableCnt; i++) {
        if (cmd_id == mtkSeries01_CmdTable[i].id)
            return (void*)(&(mtkSeries01_CmdTable[i]));
    }
    return NULL;
}

#ifdef __cplusplus
}
#endif
